/*
Copyright The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package slices

// ToMap creates a map[K]V out of the provided slice s using mf() to get the key and value
// for a given index i.
//
// The caller can compare the length of the map to the one of the slice in order to detect
// potential key conflicts.
func ToMap[K comparable, V any, S ~[]E, E any](s S, mf func(int) (K, V)) map[K]V {
	if s == nil {
		return nil
	}

	if len(s) == 0 {
		return map[K]V{}
	}

	ret := make(map[K]V, len(s))
	for i := range s {
		k, v := mf(i)
		ret[k] = v
	}
	return ret
}

// ToRefMap creates a map[K]*S out of the provided slice s []S using key() to create the map keys.
//
// The caller can compare the length of the map to the one of the slice in order to detect
// potential key conflicts.
func ToRefMap[K comparable, S ~[]E, E any](s S, key func(*E) K) map[K]*E {
	return ToMap(s,
		func(i int) (K, *E) {
			return key(&s[i]), &s[i]
		})
}

// Map creates a new slice with the same number elements as the input s with
// the values generated by mapFunc
func Map[From any, To any, S ~[]From](s S, mapFunc func(*From) To) []To {
	if s == nil {
		return nil
	}
	ret := make([]To, len(s))
	for i := range s {
		ret[i] = mapFunc(&s[i])
	}
	return ret
}

// CmpNoOrder returns true if the two provided slices have the same elements
// regardless of their order.
func CmpNoOrder[E comparable, S ~[]E](a, b S) bool {
	if len(a) != len(b) {
		return false
	}

	counters := make(map[E]int, len(a))
	for i := range a {
		counters[a[i]]++
		counters[b[i]]--
	}

	for _, v := range counters {
		if v != 0 {
			return false
		}
	}
	return true
}

// Pick creates a new slice containing only the elements for which keep return true.
func Pick[E any, S ~[]E](s S, keep func(*E) bool) S {
	var ret S
	for i := range s {
		if keep(&s[i]) {
			ret = append(ret, s[i])
		}
	}
	return ret
}

// Reduce executes a user-supplied "reducer" function on each element of the slice.
func Reduce[T, M any](s []T, f func(M, T) M, initValue M) M {
	acc := initValue
	for _, v := range s {
		acc = f(acc, v)
	}
	return acc
}
